package mechanics;

/**
 * Controller class provides an easy to use interface to the server, it returns
 * data from the sensors and sets the dimmer to the specified values
 * 
 * @version 31-03-2009
 */
public class Controller {

	// Dimmer value
	private double dimmer;

	// Groupnumber
	private int group;
	// The Memory IO
	private MemIO io;

	// The last light measurement
	private int lastLightValue;
	// The time of the last light measurement
	private int lastLightTime;

	/**
	 * Initializes the IO
	 */
	public Controller(String host) {
		// Default group to 0
		group = 0;

		// Load Memory IO
		this.io = new MemIO(host);

		this.lastLightValue = -1;
	}

	/**
	 * Sets the group to control
	 * 
	 * @param newGroup The group to control
	 */
	public void setGroup(int newGroup) {
		group = newGroup * 2;
	}

	/**
	 * Gets the current humidity
	 * 
	 * @return The current humidity
	 */
	public int getHumidity() {
		int humidity = (int) this.io.read(0x310 + this.group);

		humidity = (int) (-4 + 0.0405 * humidity - 2.8 * Math.pow(10, -6) * Math.pow(humidity, 2));

		return humidity;
	}

	/**
	 * Gets the current amount of light
	 * 
	 * @return The current amount of light
	 */
	public int getLight() {
		// Check if the last measurement was more than 2 seconds ago, if not,
		// return the last measurement
		if (this.lastLightTime - 2000 < (System.currentTimeMillis() / 1000)) {
			// Set light to -1
			int light = -1;

			// Set low and high bytes
			int lo_byte;
			int hi_byte;

			// Set Mode&Interrupt to 'Software trigger with program polling' and
			// switch to IRQ2
			this.io.write(0x22B, 0x20);
			// Select the correct group
			this.io.write(0x229, 0x00);
			// Set the A/D Input Range Control register to a gain of x1
			this.io.write(0x22C, 0xFF);

			// Read out the low byte
			lo_byte = this.io.read(0x224);
			// Read out the high byte
			hi_byte = this.io.read(0x225);

			// Check if the A/D conversion was complete and retry if not
			// hi_byte should look like xxx1xxxx (0x10 or 16)
			int tries = 0;
			while ((hi_byte & 0x10) != 16) {
				lo_byte = this.io.read(0x224);
				hi_byte = this.io.read(0x225);

				tries++;

				// Wait half a second to avoid server crashes
				try {
					Thread.sleep(500);
				} catch (InterruptedException ie) {}
			}

			// Turn the two bytes into one short
			light = hi_byte << 8 | lo_byte;
			light = light & 0xFFF;

			// Calculate the value to a percentage
			light = (light - 2046) / 21;

			// Store the value locally
			this.lastLightValue = light;

			return light;
		} else {
			return this.lastLightValue;
		}
	}

	/**
	 * Gets the current temperature
	 * 
	 * @return The current temperature
	 */
	public int getTemperature() {
		// Read the value of the temperature
		int temperature = this.io.read(0x300 + this.group);

		// Calculate the actual temperature in degrees celcius
		temperature = (int) (-40 + temperature / 100);

		return temperature;
	}

	/**
	 * Sets the dimmer's intensity to the specified percentage
	 * 
	 * @param percentage The dimmer's intensity defined as percentage
	 * @return True of the operation was successful, false if not
	 */
	public boolean setDimmer(double percentage) {
		if (percentage > 100 || percentage < 0) {
			return false;
		}

		// Set the dimmer value to the given percentage
		this.dimmer = percentage;

		// Calculate absolute value;
		// minValue = 800;
		// maxValue = 3295;
		// minValue + percentage / 100 * maxValue - minValue = absoluteValue
		int value = (int) Math.round(800 + (double) percentage / 100 * 3295);

		// Create hi byte
		byte hi_byte = (byte) (value >> 8);
		// Create low byte
		byte low_byte = (byte) value;

		// Send hi byte
		this.io.write(0x2c0 + this.group, hi_byte);
		// Send low byte
		this.io.write(0x2c1 + this.group, low_byte);

		return true;
	}

	/**
	 * Returns the value of the dimmer
	 * 
	 * @return The value of the dimmer
	 */
	public double getDimmerValue() {
		return dimmer;
	}
}
